<?php

namespace App\Console\Crons;

use App\Commons\CommonConstant;
use App\Models\Log\LogCron;

class CronRun
{
    /**
     * 任务调度实例
     * @var \Illuminate\Console\Scheduling\Schedule
     */
    protected $schedule;

    public function __construct($schedule = null)
    {
        $this->schedule = $schedule;
    }

    private function loadCron($cron_name)
    {
        $class_name = str_replace('\\', '/', $cron_name);
        $class_path = app_path("Console/Crons/{$class_name}.php");
        if (!file_exists($class_path)) {
            return null;
        }

        $class = "App\\Console\\Crons\\{$cron_name}";
        return new $class;
    }

    /**
     * 执行某个调度任务
     *
     * @param string $type 类型
     * @param string $cron_name 脚本类名
     * @param string $cron_time 任务调度频率 既/etc/crontab的时间配置 如每分钟是 * * * * *
     * @param int $run_day 执行哪一天
     * @return void
     */
    private function handle($type, $cron_name, $cron_time, $run_day)
    {
        $obj = $this->loadCron($cron_name);
        if (!$obj) {
            return;
        }

        $logcron              = new LogCron;
        $logcron->create_time = time();
        $logcron->type        = $type;
        $logcron->cron_name   = $cron_name;
        $logcron->run_day     = $run_day;
        $logcron->status      = CommonConstant::CRON_STATUS_NONE;

        $this->schedule->call(function () use ($obj, $run_day, $logcron) {
            // 任务执行
            try {
                $logcron->status = CommonConstant::CRON_STATUS_RUN;
                $logcron->save();

                $obj->run($run_day);
            } catch (\Exception $e) {
                report($e);
                $logcron->status = CommonConstant::CRON_STATUS_EXCEPTION;
                $logcron->save();
            } catch (\Error $e) {
                report($e);
                $logcron->status = CommonConstant::CRON_STATUS_ERROR;
                $logcron->save();
            }
        })->before(function () use ($logcron) {
            // 任务开始前…
            $logcron->status     = CommonConstant::CRON_STATUS_BEFORE;
            $logcron->start_time = microtime(true);
            $logcron->save();
        })->after(function () use ($logcron) {
            // 任务完成…
            if ($logcron->status == CommonConstant::CRON_STATUS_RUN) {
                $logcron->status = CommonConstant::CRON_STATUS_SUCCESS;
            }
            $logcron->end_time = microtime(true);
            $logcron->run_time = round($logcron->end_time - $logcron->start_time, 4);
            $logcron->save();
        })->cron($cron_time)->name($cron_name)->withoutOverlapping();
    }

    /**
     * 此接口由 Console/Kernel.php 调用
     * @return boolean
     */
    public function run()
    {
        $config = ['today', 'yesterday'];
        foreach ($config as $type) {
            $file = "Console/Crons/config/{$type}.php";
            $list = require app_path($file);
            switch ($type) {
                case 'yesterday':
                    $run_day = strtotime('yesterday');
                    break;
                default:
                    $run_day = strtotime('today');
                    break;
            }
            foreach ($list as $cron_time => $crons) {
                foreach ($crons as $cron_name) {
                    $this->handle($type, $cron_name, $cron_time, $run_day);
                }
            }
        }

        return true;
    }
}
